﻿using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Runtime.Serialization.Formatters.Binary;
using System.Web;
using System.Web.Security;

namespace ASPNETPerformance.App_BusinessLogic.Cache {
    public class FileCacheManager {
        private string _cachePath;

        private string CachePath {
            get {
                if ( !string.IsNullOrEmpty( _cachePath ) )
                    return _cachePath;

                _cachePath = "~/Temp/";
                if ( _cachePath == null )
                    _cachePath = "~/";
                HttpContext context = HttpContext.Current;
                if ( context != null ) {
                    _cachePath = context.Server.MapPath( _cachePath );
                    if ( !_cachePath.EndsWith( "\\" ) )
                        _cachePath += "\\";
                }



                return _cachePath;
            }
        }

        public object this[ string cacheKey ] {
            get {
                return Get( cacheKey );
            }
            set {
                Add( cacheKey, value, DateTime.Now.AddDays( 365 ) );
            }
        }

        public object Add( string key, object entry, DateTime utcExpiry ) {
            string path = CachePath + FormsAuthentication.HashPasswordForStoringInConfigFile( key, "MD5" ) + ".dat";
            if ( File.Exists( path ) )
                return entry;
            entry = AddHelper( key, entry, utcExpiry );
            return entry;
        }


        private object AddHelper( string key, object entry, DateTime utcExpiry ) {
            string path =
                CachePath + FormsAuthentication.HashPasswordForStoringInConfigFile( key, "MD5" ) + ".dat";


            using ( FileStream file = File.OpenWrite( path ) ) {
                var item = new CacheItem {
                    Expires = utcExpiry,
                    Item = entry
                };
                var formatter = new BinaryFormatter();
                formatter.Serialize( file, item );
            }

            return entry;
        }

        public object Get( string key ) {
            string path = CachePath + FormsAuthentication.HashPasswordForStoringInConfigFile( key, "MD5" ) + ".dat";
            if ( !File.Exists( path ) )
                return null;

            CacheItem item = null;
            using ( FileStream file = File.OpenRead( path ) ) {
                var formatter = new BinaryFormatter();
                item = ( CacheItem )formatter.Deserialize( file );
            }

            if ( item == null || item.Expires <= DateTime.Now.ToUniversalTime() ) {
                Remove( key );
                return null;
            }
            return item.Item;
        }

        public void Remove( string key ) {
            string path = CachePath + FormsAuthentication.HashPasswordForStoringInConfigFile( key, "MD5" ) + ".dat";
            if ( File.Exists( path ) )
                File.Delete( path );
        }

        public void Set( string key, object entry, DateTime utcExpiry ) {
            var item = new CacheItem {
                Expires = utcExpiry,
                Item = entry
            };
            string path = CachePath + FormsAuthentication.HashPasswordForStoringInConfigFile( key, "MD5" ) + ".dat";

            using ( FileStream file = File.OpenWrite( path ) ) {
                var formatter = new BinaryFormatter();
                formatter.Serialize( file, item );
            }
        }

        [Serializable]
        internal class CacheItem {
            public DateTime Expires;
            public object Item;
        }
    }
}